package org.jf.tool.fx.jftoolfx.util;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

/**
 * 对称加解密工具类
 *
 * @author jifeng_zhang
 * @date 2025/8/18 下午2:18
 */
public class SymmetricUtil {

    public static enum TYPE {
        AES("AES"),

        DES("DES"),

        DES3("DESede"),

        RC2("RC2"),

        RC4("RC4"),

        RC5("RC5"),

        RC6("RC6"),

        BLOWFISH("Blowfish"),

        TWOFISH("Twofish"),

        SM4("SM4");

        private final String name;


        TYPE(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    public static enum MODE {
        ECB("ECB"),

        CBC("CBC"),

        CTR("CTR"),

        CFB("CFB"),

        OFB("OFB");

        private final String name;

        MODE(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    public static enum PADDING {
        NoPadding("NoPadding"),

        PKCS5Padding("PKCS5Padding"),

        PKCS7Padding("PKCS7Padding"),

        ISO10126Padding("ISO10126Padding"),

        ISO7816_4Padding("ISO7816-4Padding"),

        ZeroBytePadding("ZeroBytePadding"),

        X9123Padding("X9.23Padding"),

        TBCPadding("TBCPadding");

        private final String name;

        PADDING(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /**
     * 对称加密
     *
     * @param type    加密类型
     * @param key     密钥(Base64格式)
     * @param plain   加密原文
     * @param mode    加密模式ECB/CBC
     * @param iv      初始向量(Base64编码)
     * @param padding 填充模式
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(SymmetricUtil.TYPE type, String key, byte[] plain, SymmetricUtil.MODE mode, String iv, SymmetricUtil.PADDING padding) throws Exception {
        // 1. 准备密钥
        byte[] keyBytes = Base64.getDecoder().decode(key);
        SecretKey secretKey = new SecretKeySpec(keyBytes, type.getName());

        // 2. 创建密码实例
        Cipher cipher;
        if (type == TYPE.RC4) {
            cipher = Cipher.getInstance("RC4", "BC");
        } else {
            String transformation = String.format("%s/%s/%s", type.getName(), mode.getName(), padding.getName());
            cipher = Cipher.getInstance(transformation);
        }

        // 3. 初始化
        if (mode == MODE.ECB || type == TYPE.RC4) {
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        } else {
            byte[] ivBytes = iv != null ? Base64.getDecoder().decode(iv) : new byte[16];
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        }

        // 4. 执行加密
        return cipher.doFinal(plain);
    }

    /**
     * 对称解密
     *
     * @param type      加密类型
     * @param key       密钥(Base64编码)
     * @param encrypted 密文
     * @param mode      加密模式
     * @param iv        初始向量(Base64编码，ECB模式可为null)
     * @param padding   填充模式
     * @return 解密原文
     */
    public static byte[] decrypt(TYPE type, String key, byte[] encrypted, MODE mode, String iv, PADDING padding) throws Exception {
        // 1. 准备密钥
        byte[] keyBytes = Base64.getDecoder().decode(key);
        SecretKey secretKey = new SecretKeySpec(keyBytes, type.getName());

        // 2. 创建密码实例
        Cipher cipher;
        if (type == TYPE.RC4) {
            cipher = Cipher.getInstance("RC4", "BC");
        } else {
            String transformation = String.format("%s/%s/%s", type.getName(), mode.getName(), padding.getName());
            cipher = Cipher.getInstance(transformation);
        }

        // 3. 初始化
        if (mode == MODE.ECB || type == TYPE.RC4) {
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
        } else {
            byte[] ivBytes = iv != null ? Base64.getDecoder().decode(iv) : new byte[16];
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        }

        // 4. 执行解密
        return cipher.doFinal(encrypted);
    }

    public static TYPE getType(String type) {
        if ("AES".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.AES;
        } else if ("DES".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.DES;
        } else if ("DES3".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.DES3;
        } else if ("RC2".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.RC2;
        } else if ("RC4".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.RC4;
        } else if ("RC5".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.RC5;
        } else if ("RC6".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.RC6;
        } else if ("Blowfish".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.BLOWFISH;
        } else if ("Twofish".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.TWOFISH;
        } else if ("SM4".equalsIgnoreCase(type)) {
            return SymmetricUtil.TYPE.SM4;
        }
        return null;
    }

    public static MODE getMode(String mode) {
        if ("ECB".equalsIgnoreCase(mode)) {
            return SymmetricUtil.MODE.ECB;
        } else if ("CBC".equalsIgnoreCase(mode)) {
            return SymmetricUtil.MODE.CBC;
        } else if ("CTR".equalsIgnoreCase(mode)) {
            return SymmetricUtil.MODE.CTR;
        } else if ("CFB".equalsIgnoreCase(mode)) {
            return SymmetricUtil.MODE.CFB;
        } else if ("OFB".equalsIgnoreCase(mode)) {
            return SymmetricUtil.MODE.OFB;
        }
        return null;
    }

    public static PADDING getPadding(String padding) {
        if ("NoPadding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.NoPadding;
        } else if ("PKCS5Padding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.PKCS5Padding;
        } else if ("PKCS7Padding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.PKCS7Padding;
        } else if ("ZeroBytePadding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.ZeroBytePadding;
        } else if ("ISO10126Padding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.ISO10126Padding;
        } else if ("ISO7816-4Padding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.ISO7816_4Padding;
        } else if ("X9123Padding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.X9123Padding;
        } else if ("TBCPadding".equalsIgnoreCase(padding)) {
            return SymmetricUtil.PADDING.TBCPadding;
        }
        return null;
    }
}
